package com.mikufufu.blog.security.handler;

import com.mikufufu.blog.common.annotation.Anonymous;
import com.mikufufu.blog.model.entity.Permission;
import com.mikufufu.blog.security.enums.RoleCode;
import com.mikufufu.blog.model.vo.admin.PermissionRoleListVO;
import com.mikufufu.blog.service.PermissionService;
import com.mikufufu.blog.utils.SpringUtils;
import com.mikufufu.blog.utils.StringUtils;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.condition.PatternsRequestCondition;
import org.springframework.web.servlet.mvc.condition.RequestMethodsRequestCondition;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * 实现FilterInvocationSecurityMetadataSource接口的权限控制类
 * 用于提供访问控制列表（ACL）来决定哪些请求有权限访问
 * 
 */
@Slf4j
public class FilterInvocationSecurityMetadataSourceImpl implements FilterInvocationSecurityMetadataSource {

    // 自动注入请求映射处理器映射
//    @Autowired
//    @Qualifier("requestMappingHandlerMapping")
//    private RequestMappingHandlerMapping requestMappingHandlerMapping;

    private final PermissionService permissionService = SpringUtils.getBean(PermissionService.class);
    private final RequestMappingHandlerMapping requestMappingHandlerMapping = SpringUtils.getBean("requestMappingHandlerMapping",RequestMappingHandlerMapping.class);
    private final AntPathMatcher antPathMatcher = new AntPathMatcher();

    private static List<PermissionRoleListVO> permissionRoleList;

    // 存储匿名权限列表的静态变量
    private static List<Permission> anonymousPermissionList = null;

    /**
     * 初始化匿名权限列表。
     * 遍历所有映射的请求处理方法，检查是否有@Anonymous注解，若有则添加到匿名权限列表中。
     * // @PostConstruct注解，在类实例化后执行
     */
    @PostConstruct
    private void initAnonymousPermissionList() {
        List<Permission> permissionList = new ArrayList<>();
        Map<RequestMappingInfo, HandlerMethod> handlerMethods = requestMappingHandlerMapping.getHandlerMethods();

        handlerMethods.forEach((k, v) -> {
            // 提前方法的@Anonymous注解
            boolean isMethodAnonymous = v.getMethod().isAnnotationPresent(Anonymous.class);
            if (!isMethodAnonymous) {
                return;
            }

            // 防御性检查URL模式
            PatternsRequestCondition patternsCondition = k.getPatternsCondition();
            if (patternsCondition == null || patternsCondition.getPatterns().isEmpty()) {
                return;
            }

            // 防御性检查HTTP方法
            RequestMethodsRequestCondition methodsCondition = k.getMethodsCondition();
            if (methodsCondition.getMethods().isEmpty()) {
                return;
            }

            // 为每个URL和HTTP方法组合创建独立对象
            patternsCondition.getPatterns().forEach(pattern -> {
                methodsCondition.getMethods().forEach(requestMethod -> {
                    Permission permission = new Permission();
                    permission.setPath(pattern);
                    permission.setMethod(requestMethod.name());

                    // 保留ApiOperation名称设置逻辑
                    if (v.getMethod().isAnnotationPresent(ApiOperation.class)) {
                        ApiOperation apiOperation = v.getMethod().getAnnotation(ApiOperation.class);
                        permission.setName(apiOperation.value());
                    }
                    permissionList.add(permission);
                });
            });
        });

        // 使用不可变集合保证线程安全
        anonymousPermissionList = Collections.unmodifiableList(permissionList);
        log.debug("匿名权限列表：{}", anonymousPermissionList);
    }


    /**
     * 检查给定的URL和HTTP方法是否有权限访问。
     * @param url 请求的URL
     * @param method 请求的方法，包括GET和POST等
     * @return 如果请求的URL和方法有权限，则返回true；否则返回false。
     */
    public boolean hasAnonymousPermission(String url,String method){
        // 检查是否匹配任一匿名权限列表中的权限
        return anonymousPermissionList.stream().anyMatch(permission -> antPathMatcher.match(permission.getPath(), url) && permission.getMethod().equalsIgnoreCase(method));
    }

    /**
     * 加载权限资源与权限的关系
     * {@code @PostConstruct} 注解用于在类被实例化后执行
     */
    @PostConstruct
    public void loadDataSource() {
        // 从permissionService获取角色权限列表
        permissionRoleList = permissionService.getPermissionRoleList();
        if (permissionRoleList == null || permissionRoleList.isEmpty()){
            // 如果角色权限列表为空，则调用permissionService的addAllControllerPermission方法写入所有权限
            boolean flag = permissionService.addAllControllerPermission();
            // 如果写入权限成功，则调用permissionService的grantAnonymousPermission方法写入web权限
            if(flag){
                permissionService.grantAnonymousPermission();
            }
        }
        log.debug("权限资源与权限的关系：{}", permissionRoleList);
    }

    /**
     * 清除权限资源与权限的关系
     */
    public void clearDataSource() {
        log.info("清除权限资源与权限的关系");
        permissionRoleList = null;
    }

    /**
     * 根据请求获取相应的安全配置属性
     * 此方法会根据传入的FilterInvocation对象（代表一个过滤器调用），
     * 来获取需要进行权限检查的安全配置属性集合。
     *
     * @param object FilterInvocation对象，代表一个过滤器调用。
     *               该对象包含了当前HTTP请求的相关信息。
     * @return 返回一个配置属性的集合，用于权限检查。
     *         如果没有匹配的权限配置，则返回一个包含"disable"的集合。
     * @throws IllegalArgumentException 如果参数不合法，则抛出此异常。
     */
    @Override
    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
        // 将对象转换为FilterInvocation类型，以获取请求信息
        FilterInvocation fi = (FilterInvocation) object;
        // 获取请求的URL
        String url = fi.getRequest().getRequestURI();
        // 获取请求的方法（如GET、POST）
        String method = fi.getRequest().getMethod();

        // 遍历匿名权限列表，如果匹配且请求方法为GET，则允许访问
        if (hasAnonymousPermission(url,method)){
            return SecurityConfig.createList(RoleCode.ROLE_VISITOR.getCode());
        }

        if (StringUtils.isEmpty(permissionRoleList)){
            // 如果角色权限列表为空，则加载数据源
            loadDataSource();
        }
        // 遍历角色权限列表，寻找与当前请求匹配的权限配置
        for (PermissionRoleListVO permissionRoleListVO : permissionRoleList){
            // 如果路径和方法匹配，则返回对应的权限角色列表
            if (antPathMatcher.match(permissionRoleListVO.getPath(), url) && permissionRoleListVO.getMethod().equals(method)){
                String[] roleList = permissionRoleListVO.getRoleList().toArray(new String[0]);
                return SecurityConfig.createList(roleList);
            }
        }
        // 如果没有找到匹配的权限配置，返回一个包含"disable"的集合
        return SecurityConfig.createList("disable");
    }


    /**
     * 获取所有配置属性
     * @return 返回null，因为此实现只针对具体请求提供权限配置
     */
    @Override
    public Collection<ConfigAttribute> getAllConfigAttributes() {
        return null;
    }

    /**
     * 判断是否支持指定的类
     * @param clazz 需要判断的类
     * @return 如果clazz是FilterInvocation的子类或实现类，返回true，否则返回false
     */
    @Override
    public boolean supports(Class<?> clazz) {
        // 判断类是否为FilterInvocation或其子类
        return FilterInvocation.class.isAssignableFrom(clazz);
    }
}

